const fs = require('fs');
const path = require('path');
const { promisify } = require('util');
const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');
const os = require('os');

// 使用Node.js内置的fs.promises
const fsPromises = fs.promises;

// 转换为Promise API
const readdir = promisify(fs.readdir);
const stat = promisify(fs.stat);

// 支持的图片格式
const SUPPORTED_FORMATS = ['.png', '.jpg', '.jpeg', '.webp', '.gif', '.tiff'];

// 线程数量 - 使用CPU核心数
const THREAD_COUNT = os.cpus().length;

/**
 * 检查文件是否为支持的图片类型
 * @param {string} filePath - 文件路径
 * @returns {boolean} 是否为支持的图片
 */
function isSupportedImage(filePath) {
  const ext = path.extname(filePath).toLowerCase();
  return SUPPORTED_FORMATS.includes(ext);
}

/**
 * 递归遍历目录并收集所有图片路径
 * @param {string} inputDir - 输入目录
 * @param {string} outputDir - 输出目录
 * @returns {Array} 图片处理任务数组
 */
async function collectImageTasks(inputDir, outputDir) {
  const tasks = [];

  try {
    const entries = await readdir(inputDir, { withFileTypes: true });

    for (const entry of entries) {
      const inputPath = path.join(inputDir, entry.name);
      const outputPath = path.join(outputDir, entry.name);

      if (entry.isDirectory()) {
        // 递归处理子目录并合并任务
        const subDirTasks = await collectImageTasks(inputPath, outputPath);
        tasks.push(...subDirTasks);
      } else if (entry.isFile() && isSupportedImage(inputPath)) {
        // 添加图片处理任务
        tasks.push({ inputPath, outputPath });
      }
    }
  } catch (error) {
    console.error(`收集任务出错: ${inputDir} - ${error.message}`);
  }

  return tasks;
}

/**
 * 使用工作线程池处理任务
 * @param {Array} tasks - 任务数组
 */
async function processTasksWithWorkers(tasks) {
  if (tasks.length === 0) {
    console.log('没有找到需要处理的图片');
    return;
  }

  console.log(`发现 ${tasks.length} 张图片，使用 ${THREAD_COUNT} 个线程进行处理...`);

  return new Promise((resolve) => {
    let completed = 0;
    let activeWorkers = 0;
    const taskQueue = [...tasks];

    // 创建工作线程函数
    const createWorker = () => {
      if (taskQueue.length === 0) {
        // 所有任务处理完毕
        if (--activeWorkers === 0) {
          resolve();
        }
        return;
      }

      activeWorkers++;
      const worker = new Worker(__filename, {
        workerData: taskQueue.shift()
      });

      // 接收工作线程的消息
      worker.on('message', (message) => {
        if (message.type === 'success') {
          console.log(`压缩成功: ${path.basename(message.inputPath)} (节省 ${message.savings}%)`);
        } else if (message.type === 'error') {
          console.error(`压缩失败: ${message.inputPath} - ${message.error}`);
        }

        completed++;
        // 显示进度
        if (completed % 5 === 0 || completed === tasks.length) {
          console.log(`进度: ${completed}/${tasks.length} (${Math.round(completed / tasks.length * 100)}%)`);
        }

        // 完成后继续处理下一个任务
        worker.terminate();
        createWorker();
      });

      worker.on('error', (error) => {
        console.error(`工作线程错误: ${error.message}`);
        worker.terminate();
        createWorker();
      });
    };

    // 启动指定数量的工作线程
    for (let i = 0; i < THREAD_COUNT; i++) {
      createWorker();
    }
  });
}

/**
 * 确保输出目录存在，如果不存在则创建
 * @param {string} outputDir - 输出目录路径
 */
async function ensureOutputDirectory(outputDir) {
  try {
    // 检查目录是否存在
    await stat(outputDir);
    console.log(`输出目录已存在: ${outputDir}`);
  } catch (error) {
    if (error.code === 'ENOENT') {
      // 目录不存在，创建目录（包括所有父目录）
      await fsPromises.mkdir(outputDir, { recursive: true });
      console.log(`输出目录不存在，已自动创建: ${outputDir}`);
    } else {
      // 其他错误（如权限问题）
      throw new Error(`无法访问输出目录: ${error.message}`);
    }
  }
}

/**
 * 主函数
 * @param {string} inputDir - 输入目录路径
 * @param {string} outputDir - 输出目录路径
 */
async function main(inputDir, outputDir) {
  if (!inputDir || !outputDir) {
    console.error('请提供输入目录和输出目录');
    console.log('用法: node image-compressor.js <输入目录> <输出目录>');
    process.exit(1);
  }

  // 解析为绝对路径
  const resolvedInput = path.resolve(inputDir);
  const resolvedOutput = path.resolve(outputDir);

  try {
    // 检查输入目录是否存在
    await stat(resolvedInput);
  } catch (error) {
    console.error(`输入目录不存在: ${resolvedInput}`);
    process.exit(1);
  }

  console.log(`开始图片压缩...`);
  console.log(`输入目录: ${resolvedInput}`);
  console.log(`输出目录: ${resolvedOutput}`);
  console.log('------------------------------');

  try {
    // 确保输出目录存在（核心改进点）
    await ensureOutputDirectory(resolvedOutput);

    // 收集所有图片处理任务
    const tasks = await collectImageTasks(resolvedInput, resolvedOutput);

    // 使用多线程处理任务
    await processTasksWithWorkers(tasks);

    console.log('------------------------------');
    console.log('所有图片处理完成!');
  } catch (error) {
    console.error('处理过程中发生错误:', error.message);
    process.exit(1);
  }
}

// 工作线程逻辑 - 只在工作线程中执行
if (!isMainThread) {
  const sharp = require('sharp');
  const { inputPath, outputPath } = workerData;

  // 工作线程执行压缩任务
  async function workerTask() {
    try {
      const ext = path.extname(inputPath).toLowerCase();
      const image = sharp(inputPath);

      // 确保当前图片的输出目录存在（处理子目录结构）
      const outputDir = path.dirname(outputPath);
      await fsPromises.mkdir(outputDir, { recursive: true });

      // 对于不同格式使用最佳压缩参数
      switch (ext) {
        case '.png':
          await image.png({
            quality: 80,
            compressionLevel: 6,
            force: false
          }).toFile(outputPath);
          break;

        case '.jpg':
        case '.jpeg':
          await image.jpeg({
            quality: 80,
            mozjpeg: true,
            force: false
          }).toFile(outputPath);
          break;

        case '.webp':
          await image.webp({
            quality: 80,
            force: false
          }).toFile(outputPath);
          break;

        case '.gif':
          await image.gif({
            quality: 80,
            force: false
          }).toFile(outputPath);
          break;

        case '.tiff':
          await image.tiff({
            quality: 80,
            force: false
          }).toFile(outputPath);
          break;

        default:
          throw new Error(`不支持的图片格式: ${ext}`);
      }

      // 计算压缩前后的文件大小
      const originalSize = (await stat(inputPath)).size;
      const compressedSize = (await stat(outputPath)).size;
      const savings = ((originalSize - compressedSize) / originalSize * 100).toFixed(2);

      // 向主线程发送成功消息
      parentPort.postMessage({
        type: 'success',
        inputPath,
        savings
      });
    } catch (error) {
      // 向主线程发送错误消息
      parentPort.postMessage({
        type: 'error',
        inputPath,
        error: error.message
      });
    }
  }

  // 执行工作线程任务
  workerTask();
} else {
  // 主线程逻辑 - 从命令行获取参数
  const [, , inputDir, outputDir] = process.argv;
  main(inputDir, outputDir);
}
